/******************************************************************************
 * * utility.c - utilities implementation
 *
 * *(C) Copyright 2019 Asr International Ltd.
 * * All Rights Reserved
 * ******************************************************************************/
#include "utility.h"

#define	LEAP_YEAR(year)		((year) % 4 == 0)
#define	DAYS_IN_YEAR(a)		(LEAP_YEAR(a) ? 366 : 365)
#define	DAYS_IN_MONTH(a)	(month_days[(a) - 1])
//#define	WEEKDAY_TO_STRING(a)	(week_days[(a)])

static INT32 month_days[12] = {
	31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

UINT8 UI_Rtc_Calc_Weekday(hal_rtc_t *tm)
{
	INT32 leapsToDate;
	INT32 lastYear;
	INT32 day;
	INT32 MonthOffset[] = { 0,31,59,90,120,151,181,212,243,273,304,334 };

	if (tm->tm_year < 1753)
    {
        return 0;
    }
    
	lastYear=tm->tm_year-1;

	/*
	 * Number of leap corrections to apply up to end of last year
	 */
	leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;

	/*
	 * This year is a leap year if it is divisible by 4 except when it is
	 * divisible by 100 unless it is divisible by 400
	 *
	 * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
	 */
	if((tm->tm_year%4==0) &&
	   ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
	   (tm->tm_mon>2)) {
		/*
		 * We are past Feb. 29 in a leap year
		 */
		day=1;
	} else {
		day=0;
	}

	day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday;

	tm->tm_wday=day%7;

    return(tm->tm_wday);
}

/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
 * param (in) tm: hal_rtc_t
 * return UINT32: time in seconds since 1970
 */
UINT32 UI_Time_to_Seconds(const hal_rtc_t *tm)
{
	INT32 mon = tm->tm_mon;
	INT32 year = tm->tm_year;
	INT32 days, hours;

	mon -= 2;
	if (0 >= (INT32)mon) {	/* 1..12 -> 11,12,1..10 */
		mon += 12;		/* Puts Feb last since it has leap day */
		year -= 1;
	}

	days = (unsigned long)(year / 4 - year / 100 + year / 400 +
			367 * mon / 12 + tm->tm_mday) +
			year * 365 - 719499;
	hours = days * 24 + tm->tm_hour;
	return (hours * 60 + tm->tm_min) * 60 + tm->tm_sec;
}

/* Converts seconds to Gregorian date to seconds since 1970-01-01 00:00:00.
 * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
 * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
 * param (out) tim: INT32 time in seconds
 * param (in) tm: hal_rtc_t  time in date format
 */
VOID UI_Seconds_to_Time(INT32 tim, hal_rtc_t *tm)
{
	INT32    i;
	INT32  hms, day;

	day = tim / SECDAY;
	hms = tim % SECDAY;

	/* Hours, minutes, seconds are easy */
	tm->tm_hour = hms / 3600;
	tm->tm_min = (hms % 3600) / 60;
	tm->tm_sec = (hms % 3600) % 60;

	/* Number of years in days */
	for (i = STARTOFTIME; day >= DAYS_IN_YEAR(i); i++) {
		day -= DAYS_IN_YEAR(i);
	}
	tm->tm_year = i;

	/* Number of months in days left */
	if (LEAP_YEAR(tm->tm_year)) {
		DAYS_IN_MONTH(FEBRUARY) = 29;
	}
	for (i = 1; day >= DAYS_IN_MONTH(i); i++) {
		day -= DAYS_IN_MONTH(i);
	}
	DAYS_IN_MONTH(FEBRUARY) = 28;
	tm->tm_mon = i;

	/* Days are what is left over (+1) from all that. */
	tm->tm_mday = day + 1;

    UI_Rtc_Calc_Weekday(tm);
}

/**
 * Get time in seconds 
 * param void:
 * return UINT32  time in seconds from 1970
 */
UINT32 UI_Get_Timestamp_In_Second(VOID)
{
    hal_rtc_t Time;
    
    Hal_Rtc_Gettime(&Time);

    return(UI_Time_to_Seconds(&Time));
} 
